package com.msb.im.netty.server;

import com.msb.im.netty.ImProperties;
import com.msb.im.netty.ImServerProperties;
import com.msb.im.netty.handler.server.ServerChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;


/**
 * WebSocket服务器实现类
 * 基于Netty框架实现的WebSocket服务器，支持即时通讯功能
 * 继承ServerAsClientAble类，具备作为客户端连接其他服务器的能力
 * 实现IServer接口，提供标准的服务器操作方法
 *
 * @description: WebSocket服务器核心实现，负责启动、管理和关闭WebSocket服务
 * @author: zhou miao
 * @create: 2022/04/08
 */
@Slf4j
public class WebSocketServer extends ServerAsClientAble implements IServer {

    /**
     * 服务器启动状态标识，使用volatile保证多线程可见性
     * 静态变量，表示整个应用中WebSocket服务器的启动状态
     */
    private static volatile boolean isStart;

    /**
     * 服务器监听端口号
     */
    private int port;

    /**
     * 服务器IP地址
     */
    private String ip;

    /**
     * Netty Boss线程组，负责接收客户端连接
     * Boss线程组专门处理ServerSocketChannel的accept事件
     */
    private NioEventLoopGroup boos;

    /**
     * Netty Worker线程组，负责处理客户端的读写操作
     * Worker线程组处理SocketChannel的读写事件
     */
    private NioEventLoopGroup worker;

    /**
     * 服务器Channel，代表服务器的监听套接字
     */
    private Channel serverChannel;

    /**
     * 服务器Channel初始化器，用于配置新连接的处理管道
     * 负责为每个新的客户端连接设置处理器链
     */
    private ServerChannelInitializer serverChannelInitializer;

    /**
     * 服务器线程池，主要用来执行作为客户端连接其他服务器的任务
     * 支持服务器间的内部通信和集群功能
     */
    private ExecutorService taskExecutorService;

    /**
     * IM服务器配置属性，包含端口、线程数、超时时间等配置信息
     */
    private ImProperties imServerProperties;

    /**
     * 同步锁对象，用于保证服务器启动过程的线程安全
     */
    private final Object lock = new Object();

    /**
     * 默认无参构造函数
     * 用于Spring等框架的依赖注入场景
     */
    public WebSocketServer() {
    }

    /**
     * 带参数的构造函数，用于初始化WebSocket服务器
     *
     * @param serverChannelInitializer Channel初始化器，配置新连接的处理管道
     * @param properties IM服务器配置属性，包含端口、线程数等配置
     * @param taskExecutorService 任务执行线程池，用于异步任务处理
     * @throws UnknownHostException 当无法获取本机IP地址时抛出异常
     */
    public WebSocketServer(ServerChannelInitializer serverChannelInitializer, ImServerProperties properties, ExecutorService taskExecutorService) throws UnknownHostException {
        // 设置Channel初始化器
        this.serverChannelInitializer = serverChannelInitializer;
        // 设置服务器配置属性
        this.imServerProperties = properties;
        // 获取本机IP地址
        this.ip = InetAddress.getLocalHost().getHostAddress();
        // 设置服务器监听端口
        this.port = properties.getPort();
        // 初始化Boss线程组，用于接收客户端连接
        this.boos = new NioEventLoopGroup(properties.getBossNthreads());
        // 初始化Worker线程组，用于处理客户端读写操作
        this.worker = new NioEventLoopGroup(properties.getWorkerNthreads());
        // 设置任务执行线程池
        this.taskExecutorService = taskExecutorService;
    }

    /**
     * 设置服务器配置属性
     * 实现IServer接口方法，用于动态更新服务器配置
     *
     * @param imProperties IM服务器配置属性对象
     */
    @Override
    public void setProperties(ImProperties imProperties) {
        this.imServerProperties = imProperties;
    }

    /**
     * 获取服务器配置属性
     * 实现IServer接口方法，返回当前服务器的配置信息
     *
     * @return 当前的IM服务器配置属性对象
     */
    @Override
    public ImProperties getProperties() {
        return imServerProperties;
    }

    /**
     * 获取服务器监听端口号
     * 实现IServer接口方法
     *
     * @return 服务器监听的端口号
     */
    @Override
    public int getPort() {
        return port;
    }

    /**
     * 设置服务器监听端口号
     *
     * @param port 要设置的端口号
     */
    public void setPort(int port) {
        this.port = port;
    }

    /**
     * 设置服务器IP地址
     *
     * @param ip 要设置的IP地址
     */
    public void setIp(String ip) {
        this.ip = ip;
    }

    /**
     * 获取服务器IP地址
     * 实现IServer接口方法
     *
     * @return 服务器的IP地址
     */
    @Override
    public String getIp() {
        return ip;
    }


    /**
     * 关闭WebSocket服务器
     * 实现IServer接口方法，优雅地关闭服务器和相关资源
     * 按顺序关闭Boss线程组、Worker线程组和任务执行线程池
     */
    @Override
    public void shutdown() {
        // 优雅关闭Boss线程组，停止接收新的客户端连接
        if (boos != null) {
            boos.shutdownGracefully();
        }

        // 优雅关闭Worker线程组，停止处理客户端读写操作
        if (worker != null) {
            worker.shutdownGracefully();
        }

        // 关闭任务执行线程池，停止异步任务处理
        if (taskExecutorService != null) {
            taskExecutorService.shutdown();
        }
    }

    /**
     * 启动WebSocket服务器
     * 实现IServer接口方法，使用Netty框架启动WebSocket服务
     * 使用同步锁确保启动过程的线程安全性，防止重复启动
     *
     * @param startedCallback 启动成功后的回调函数，可以为null
     */
    @Override
    public void start(Runnable startedCallback) {
        // 使用同步锁保证启动过程的线程安全
        synchronized (lock) {
            try {
                // 检查服务器是否已经启动，防止重复启动
                if (isStart()) {
                    log.error("重复启动 {}", this);
                    return;
                }

                // 创建Netty服务器启动器
                ServerBootstrap b = new ServerBootstrap();

                // 配置服务器启动参数
                b.group(boos, worker)                                           // 设置Boss和Worker线程组
                        .channel(NioServerSocketChannel.class)                  // 设置服务器Channel类型为NIO
                        .childHandler(serverChannelInitializer)                 // 设置子Channel的处理器初始化器
                        .option(ChannelOption.SO_BACKLOG, imServerProperties.getSoBackLog())  // 设置服务器socket的连接队列大小
                        .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE); // 启用TCP keepalive机制

                // 绑定端口并同步等待绑定完成
                ChannelFuture channelFuture = b.bind(port).sync();

                // 获取服务器Channel
                serverChannel = channelFuture.channel();

                // 更新服务器IP地址（防止配置变更）
                this.ip = InetAddress.getLocalHost().getHostAddress();

                // 记录服务器启动成功日志
                log.info("服务器 {}:{} 启动", getIp(), getPort());

                // 设置启动状态标识
                isStart = true;

                // 执行启动成功回调
                if (startedCallback != null) {
                    startedCallback.run();
                }

                // 监听服务器Channel关闭事件
                serverChannel.closeFuture().addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture channelFuture) throws Exception {
                        // 服务器Channel关闭时，执行清理操作
                        shutdown();
                        log.error("服务器关闭...");
                        // 退出应用程序
                        System.exit(1);
                    }
                });
            } catch (Exception e) {
                // 启动失败时，执行清理操作
                shutdown();
            }
        }
    }

    /**
     * 检查服务器是否已启动
     * 实现IServer接口方法
     *
     * @return true表示服务器已启动，false表示服务器未启动
     */
    @Override
    public boolean isStart() {
        return isStart;
    }

    /**
     * 重写toString方法，提供服务器的字符串表示
     * 主要用于日志输出和调试
     *
     * @return 包含服务器IP和端口信息的字符串
     */
    @Override
    public String toString() {
        return "WebSocketServer{" +
                "port=" + port +
                ", ip='" + ip + '\'' +
                '}';
    }

    /**
     * 获取任务执行线程池
     * 继承自ServerAsClientAble类的方法实现
     * 用于执行异步任务，特别是作为客户端连接其他服务器的任务
     *
     * @return 任务执行线程池对象
     */
    @Override
    public ExecutorService getTaskExecutorService() {
        return taskExecutorService;
    }
}